/*
 * Created by Jaren at 2021/8/21 15:32
 */
import 'dart:io';
import 'dart:typed_data';

import 'package:inmotion_protocol/base/base_message.dart';
import 'package:inmotion_protocol/common/exceptions.dart';
import 'package:inmotion_protocol/common/product_model.dart';
import 'package:inmotion_protocol/common/product_version.dart';
import 'package:inmotion_protocol/common/response.dart';
import 'package:inmotion_protocol/common/vehicle_info.dart';
import 'package:inmotion_protocol/ezcan/ezcan_constant.dart';
import 'package:inmotion_protocol/ezcan/ezcan_message.dart';
import 'package:inmotion_protocol/ezcan/file/response.dart';
import 'package:inmotion_protocol/util/byte_util.dart';
import 'package:inmotion_protocol/util/file.dart';

class EzcanUtil {
  static int sumCheck1Byte(Uint8ClampedList bytes, int offset, int length) {
    int sum = 0x00;
    for (int i = offset, totalLength = offset + length; i < totalLength; i++) {
      sum += (bytes[i] & 0xFF);
    }

    return sum & 0xFF;
  }

  static ProductProperty parseInitInfoFromSlowMsg(EzcanMessage slowRspMsg) {
    if (slowRspMsg.extra == null) throw ProtocolParseException("extra is null");
    Uint8ClampedList bytes = slowRspMsg.extra!;
    String sn = bytes[0] == 0 ? ProductProperty.unknownSn : ByteUtil.bytes2HexStringLe(bytes, 0, 8).toUpperCase();
    ProductModel model = ProductModel.fromEzcan(bytes, 104);
    int pcVersionInt = ByteUtil.bytes2Uint32Le(bytes, 24).value;
    int mcVersionInt = ByteUtil.bytes2Uint32Le(bytes, 32).value;
    String pcAppVersion = "${pcVersionInt >> 24}.${pcVersionInt >> 16 & 0xFF}.${pcVersionInt & 0xFFFF}";
    String mcAppVersion = "${mcVersionInt >> 24}.${mcVersionInt >> 16 & 0xFF}.${mcVersionInt & 0xFFFF}";
    Version pcVersion = Version(ByteUtil.toUint8(getPcDevice(model)), pcAppVersion, "", "");
    Version mcVersion = Version(ByteUtil.toUint8(getMcDevice(model)), mcAppVersion, "", "");
    VersionWrapper versionWrapper = VersionWrapper();
    versionWrapper.versions[pcVersion.device.value] = pcVersion;
    versionWrapper.versions[mcVersion.device.value] = mcVersion;

    return ProductProperty(sn, model, null, versionWrapper);
  }

  static SettingsResponse parseSettingsRsp(EzcanMessage rspMsg) {
    int error = ByteUtil.bytes2IntLe(rspMsg.data, 0, 4);
    if (error == 1) return SettingsResponse.success();
    if (error == 0x2E) return SettingsResponse.invalidParam();
    return SettingsResponse.unknown();
  }

  static EzCANFileResponse parseFileRsp(EzcanMessage rspMsg) {
    int error = ByteUtil.bytes2IntLe(rspMsg.data, 0, 4);
    if (error == 1) return EzCANFileResponse.success(originError: 1);
    return EzCANFileResponse.byOriginError(error);
  }

  static SettingsResponse parseAssistanceSettingsRsp(EzcanMessage sentMsg, EzcanMessage rspMsg) {
    if (ByteUtil.arrayEquals(sentMsg.data, rspMsg.data)) return SettingsResponse.success();
    return SettingsResponse.unknown();
  }

  static bool isQuickConnectRequestMsg(IMessage msg) {
    return msg is EzcanMessage && msg.id.value == EzcanConstant.ID_QUICK_CONNECT_REQUST;
  }

  static int getPcDevice(ProductModel model) {
    if (model.isEuc()) return EzcanConstant.EUC_DEVICE_PC;
    if (model.isScooter()) return EzcanConstant.SCOOTER_DEVICE_PC;
    if (model.isEBike()) return EzcanConstant.E_BIKE_DEVICE_PC;
    if (model.isDulWheel()) return EzcanConstant.DUL_WHEEL_DEVICE_PC;
    return 0;
  }

  static int getMcDevice(ProductModel model) {
    if (model.isEuc()) return EzcanConstant.EUC_DEVICE_MC;
    if (model.isScooter()) return EzcanConstant.SCOOTER_DEVICE_MC;
    if (model.isEBike()) return EzcanConstant.E_BIKE_DEVICE_MC;
    if (model.isDulWheel()) return EzcanConstant.DUL_WHEEL_DEVICE_MC;
    return 0;
  }
}

class EzCANFileUtils {
  static int calculateFileSum(File file, [int from = 0, int length = -1, Uint8ClampedList? others]) {
    int sum = 0;
    Uint8ClampedList contents = FileUtils.getFileContentSync(file, -1);
    if (length == -1) length = contents.length;
    if (contents.isEmpty || contents.length < from + length) {
      throw '';
    }
    for (int i = from; i < from + length; i++) {
      sum += contents[i];
    }
    if (others != null && others.isNotEmpty) {
      for (var element in others) {
        sum += element;
      }
    }
    sum &= 0xFF;
    return ByteUtil.toInt8(sum).value;
  }

  static int calculateSum(List<int> bytes, [int offset = 0, int length = -1, Uint8ClampedList? others]) {
    int sum = 0;
    if (length == -1) length = bytes.length;
    if (bytes.isEmpty || bytes.length < offset + length) {
      throw Exception('Invalid bytes to calculate file sum');
    }
    for (int i = offset; i < offset + length; i++) {
      sum += bytes[i];
    }

    if (others != null && others.isNotEmpty) {
      for (var element in others) {
        sum += element;
      }
    }

    sum &= 0xFF;
    return ByteUtil.toInt8(sum).value;
  }

  static int calculateUpdateFileSum(File file) {
    Uint8ClampedList contents = FileUtils.getFileContentSync(file, -1);
    if (contents.isEmpty) {
      throw '';
    }
    int checksum = 0x00;
    for (int i = 0; i < contents.length - 1; i++) {
      checksum ^= contents[i];
    }
    return checksum & 0xFF;
  }
}
